﻿/**
 * Copyright (C) 2011 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using NGuice.Inject.Internal.Util;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// 绑定值或常数的构建器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class AbstractBindingBuilder<T>
    {
        public  const string IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
        
        public  const string SINGLE_INSTANCE_AND_SCOPE = "Setting the scope is not permitted when binding to a single instance.";
        
        public  const string SCOPE_ALREADY_SET = "Scope is set more than once.";
        
        public  const string BINDING_TO_NULL = "Binding to null instances is not allowed. "
                                                      + "Use ToProvider(Providers.of(null)) if this is your intended behaviour.";
        
        public  const string CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
        
        public  const string ANNOTATION_ALREADY_SPECIFIED = "More than one annotation is specified for this binding.";

        protected internal static readonly Key NULL_KEY = Key<T>.Get();
        
        protected IList<IElement> elements;
        
        protected int position;
        
        protected readonly IBinder binder;
        
        private BindingImpl<T> binding;

        public AbstractBindingBuilder(IBinder binder, IList<IElement> elements, object source, Key<T> key) 
        {
            this.binder = binder;
            this.elements = elements;
            this.position = elements.Count;
            this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED);
            elements.Insert(position, this.binding);
        }

        protected virtual BindingImpl<T> Binding 
        {
            get 
            {
                 return binding;
            }
            set
            {
                this.binding = value;
                elements[position] = binding;
                //elements.Insert(position, binding);
            }
        }
        
        /// <summary>
        /// 设置binding为具有key的特定元特性的copy
        /// </summary>
        /// <param name="attributeType"></param>
        /// <returns></returns>
        protected virtual BindingImpl<T> AnnotatedWithInternal(Type attributeType) 
        {
            Preconditions.CheckNotNull(attributeType, "attributeType");
            CheckNotAnnotated();
            return Binding = binding.WithKey(Key<T>.Get(attributeType));
        }

        protected virtual BindingImpl<T> AnnotatedWithInternal(Attribute attribute) 
        {
            Preconditions.CheckNotNull(attribute, "attribute");
            CheckNotAnnotated();
            return Binding = binding.WithKey(Key<T>.Get(attribute));
        }

        public virtual void In(Type scopeAttribute) 
        {
            Preconditions.CheckNotNull(scopeAttribute, "scopeAttribute");
            CheckNotScoped();
            Binding = Binding.WithScoping(Scoping.ForAnnotation(scopeAttribute));
        }

        public virtual void In(IScope scope) 
        {
            Preconditions.CheckNotNull(scope, "scope");
            CheckNotScoped();
            Binding = Binding.WithScoping(Scoping.ForInstance(scope));
        }

        public virtual void AsEagerSingleton()
        {
            CheckNotScoped();
            Binding = Binding.WithScoping(Scoping.EAGER_SINGLETON);
        }

        protected virtual bool KeyTypeIsSet() 
        {
            return !typeof(AsyncVoid).Equals(binding.Key.TypeLiteral);
        }

        protected virtual void CheckNotTargetted()
        {
            if (!(binding is UntargettedBindingImpl<T>))
            {
                binder.AddError(IMPLEMENTATION_ALREADY_SET);
            }
        }

        protected virtual void CheckNotAnnotated() 
        {
            if (binding.Key.AttributeType != null)
            {
                binder.AddError(ANNOTATION_ALREADY_SPECIFIED);
            }
         }

        protected virtual void CheckNotScoped() 
        {
            // Scoping isn't allowed when we have only one instance.
            //当仅有一个实例时，scoping不被允许使用
            if (binding is IInstanceBinding<T>) 
            {
              binder.AddError(SINGLE_INSTANCE_AND_SCOPE);
              return;
            }
            if (binding.Scoping.IsExplicitlyScoped) 
            {
                binder.AddError(SCOPE_ALREADY_SET);
            }
        }
    }
}
